#pragma once

#include <string>
#include <list>
#include <map>
#include <sstream>
#include <boost/cstdint.hpp>
#include <boost/variant.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/find.hpp>

namespace bitgene {

namespace detail {

template <typename T>
struct bencode_traits_vector
{
	typedef typename boost::mpl::vector4<
		  typename T::integer_type
		, typename T::string_type
		, typename T::list_type
		, typename T::dictionary_type
	>::type type;
};

} // namespace detail

template <template <typename> class Traits>
struct basic_bencode
{

	typedef typename Traits<basic_bencode>::integer_type integer_type;
	typedef typename Traits<basic_bencode>::string_type string_type;
	typedef typename Traits<basic_bencode>::list_type list_type;
	typedef typename Traits<basic_bencode>::dictionary_type dictionary_type;

	basic_bencode() {}

	template <typename T>
	basic_bencode(const T& v): variant_(v) {}

	template <typename T>
	basic_bencode & operator =(const T& v)
	{
		typedef typename bencode_type_map<T>::type type;
		variant_ = reinterpret_cast<const type &>(v);
		return *this;
	}

	template <typename T>
	T * pointer()
	{
		typedef typename bencode_type_map<T>::type type;
		return reinterpret_cast<T *>(boost::get<type>(&variant_));
	}

	template <typename T>
	const T * const_pointer() const
	{
		typedef typename bencode_type_map<T>::type type;
		return reinterpret_cast<const T *>(boost::get<type>(&variant_));
	}

	template <typename T>
	T & reference()
	{
		typedef typename bencode_type_map<T>::type type;
		return reinterpret_cast<T &>(boost::get<type>(variant_));
	}

	template <typename T>
	const T & const_reference() const
	{
		typedef typename bencode_type_map<T>::type type;
		return reinterpret_cast<const T &>(boost::get<type>(variant_));
	}

private:

	typedef typename boost::make_recursive_variant_over<
		typename detail::bencode_traits_vector<
			Traits<boost::recursive_variant_>
		>::type
	>::type variant_type;

	typedef typename detail::bencode_traits_vector<
		Traits<variant_type>
	>::type variant_types;

	typedef typename detail::bencode_traits_vector<
		Traits<basic_bencode>
	>::type bencode_types;

	template <typename T>
	struct bencode_type_map: public boost::mpl::at<
		  variant_types
		, typename boost::mpl::find<bencode_types, T>::type::pos
		>
	{};

	variant_type variant_;

};

namespace detail {

template <typename T>
struct default_bencode_traits
{
	typedef boost::intmax_t integer_type;
	typedef std::string string_type;
	typedef std::list<T> list_type;
	typedef std::map<std::string, T> dictionary_type;
};

} // namespace detail

typedef basic_bencode<detail::default_bencode_traits> bencode;

} // namespace bitgene
